/* ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*
* Quotek - Sterownik Pompy Ciepla, Projekt DIY
* http://www.quotek.pl
*
* Copyright (C) 2012-2013 Piotr Murawski
*
* Niniejszy program jest wolnym oprogramowaniem; mozesz go
* rozprowadzac dalej i/lub modyfikowac na warunkach Powszechnej
* Licencji Publicznej GNU, wydanej przez Fundacje Wolnego
* Oprogramowania - wedlug wersji 3-ciej tej Licencji lub kt�rejs
* z pozniejszych wersji.
*
* Niniejszy program rozpowszechniany jest z nadzieja iz bedzie on
* uzyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyslnej
* gwarancji PRZYDATNOSCI HANDLOWEJ albo PRZYDATNOSCI DO OKRESLONYCH
* ZASTOSOWAN. W celu uzyskania blizszych informacji - Powszechna
* Licencja Publiczna GNU.
*
* Z pewnoscia wraz z niniejszym programem otrzymales tez egzemplarz
* Powszechnej Licencji Publicznej GNU (GNU General Public License);
* jesli nie - napisz do Free Software Foundation, Inc., 675 Mass Ave,
* Cambridge, MA 02139, USA.
*
* Powszechna Licencja GNU znajduje sie w zalaczonym pliku:
* Licencja.txt
*
* Powszechna Licencja GNU dostepna jest rowniez na stronie:
* http://www.gnu.org/licenses/licenses.html
*
*
* nieoficjalne polskie tlumaczenie na
* http://www.gnu.org.pl
*
* ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*
* Quotek - Heat Pump Controller (DIY)
* http://www.quotek.pl
*
* Copyright (C) 2012 Piotr Murawski
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/


#include <fcntl.h>
#include <termios.h>
#include <netinet/in.h>

#include <stdio.h>
#include <libxml/xmlreader.h>

#include "top.h"
#include "config_struct.h"
#include "program_struct.h"
#include "temperatury_definicje.h"
#include "temperatury.h"
#include "xmlutil.h"
#include "krzywe_struct.h"
#include "krzywa.h"


static int processNode(xmlTextReaderPtr reader, TKrzKrzywa*  pkrzy);
static U8 czyt_Int(char* pAtrName, char* pName, char* pValue, int* pVal, U8* pErr);
static U8 czyt_Float(char* pAtrName, char* pName, char* pValue, float* pVal, U8* pErr);
static int czytkrzy_CzytajWsp(char* pName, char* pValue, TKrzWsp* pwsp, int* mask);
static int czytkrzy_CzytajOdcinek(xmlTextReaderPtr reader, TKrzOdcinek* podc);
static int czytkrzy_CzytajKorekcje(xmlTextReaderPtr reader, TKrzKrzywa*  pkrzy);
static int czytkrzy_CzytajPunkt(xmlTextReaderPtr reader, TKrzPunkt* pp);
static int czytkrzy_CzytajTempWej(xmlTextReaderPtr reader, TKrzTemp* ptin);


/* CZYTKRZY_CzytajPlik
 *
 */
int CZYTKRZY_CzytajPlik(char* fname, TKrzKrzywa*  pkrzy)
{
    xmlTextReaderPtr reader;
    int ret=OK;

    reader = xmlReaderForFile(fname, NULL, 0);
    
    if (reader != NULL) {
        ret = xmlTextReaderRead(reader);
        while (ret == 1) {
            ret= processNode(reader, pkrzy);
            if(ret != 0){
                ERR("Blad tresci lub skladni %s \n",XMLU_GetParserLocation(reader));
                return KO;
            }
            ret = xmlTextReaderRead(reader);
        }
        if (ret != 0) {
            xmlNodePtr lvCurrXmlNode=xmlTextReaderCurrentNode(reader);
            ERR("Nie moge zrozumiec pliku XML konfiguracji w lini: %d\n", lvCurrXmlNode->line);
            ret=KO;
        }
        xmlFreeTextReader(reader);
    }else{
        ERR("Nie moge otworzyc \"%s\"n", fname);
        ret=KO;
    }


    return ret;
}




int processNode(xmlTextReaderPtr reader, TKrzKrzywa*  pkrzy)
{
    const xmlChar *name, *value;
    const char* txt;
    int   depth;
    int   nodetype;
    int   ret;
    static int   linKrzywa;
    static int   idx;
    static TKrzKrzywa*  ppkrza = NULL;
    S16 lastTemp;

    name = xmlTextReaderConstName(reader);
    if (name == NULL) name = "--";

    value = xmlTextReaderConstValue(reader);

    switch(xmlTextReaderNodeType(reader))
    {
      case 2 : txt="Attribute            "; break;
      case 4 : txt="CDATA                "; break;
      case 8 : txt="Comment              "; break;
      case 9 : txt="Document             "; break;
      case 11: txt="DocumentFragment     "; break;
      case 10: txt="DocumentType         "; break;
      case 1 : txt="Element              "; break;
      case 15: txt="EndElement           "; break;
      case 16: txt="EndEntity            "; break;
      case 6 : txt="Entity               "; break;
      case 5 : txt="EntityReference      "; break;
      case 0 : txt="None                 "; break;
      case 12: txt="Notation             "; break;
      case 7 : txt="ProcessingInstruction"; break;
      case 14: txt="SignificantWhitespace"; break;
      case 3 : txt="Text                 "; break;
      case 13: txt="Whitespace           "; break;
      case 17: txt="XmlDeclaration       "; break;
      default: txt="??????               "; break;
    }


    depth = xmlTextReaderDepth(reader);
    nodetype = xmlTextReaderNodeType(reader);

    if((depth==0)&&(nodetype==1)&&(strcmp(name,"pompa")==0)){
       linKrzywa=0;
       idx=0;
       ppkrza = pkrzy;
    }

    if(ppkrza){
       ret=CZYT_ProcessKrzywa(reader, name, &ppkrza, &linKrzywa, depth, nodetype, &idx);
       if(ret) return ret;
    }

    return 0;
}





static int czytkrzy_CzytajOdcinek(xmlTextReaderPtr reader, TKrzOdcinek* podc)
{
   char* aname;
   char* avalue;
   int ret=OK;
   int mask=0;
   U8     hErr;
   float  hFl;

   while(xmlTextReaderMoveToNextAttribute(reader))
   {
      aname  = (char*)xmlTextReaderName(reader);
      avalue = (char*)xmlTextReaderValue(reader);

      if(czytkrzy_CzytajWsp(aname, avalue, &podc->wsp, &mask)!=OK){
    	  return KO;
      }

	  if(czyt_Float("temp", aname, avalue, &hFl,&hErr)){
		  if((hErr) || (hFl<-50.0) || (hFl>100.0)){
			  ERR("Nie wartosc temperatury granicznej %s=\"%s\"",aname,avalue);
			  return KO;
		  }
		  hFl*=10.0;
		  podc->tempGr=hFl;
          mask|=1;
	  }
   }

   //IF brak temperatury - ostatni odcinek
   if(mask == 6){
	   podc->tempGr=KRZY_TEMPGRLAST;
       return OK;
   }else
   if(mask != 7){
       ERR("Brak wszystkich wymaganych atrybutow");
	   return KO;
   }

   return OK;
}




static int czytkrzy_CzytajPunkt(xmlTextReaderPtr reader, TKrzPunkt* pp)
{
   char* aname;
   char* avalue;
   int ret=OK;
   int mask=0;
   U8     hErr;
   float  hFl;

   while(xmlTextReaderMoveToNextAttribute(reader))
   {
      aname  = (char*)xmlTextReaderName(reader);
      avalue = (char*)xmlTextReaderValue(reader);

	  if(czyt_Float("tempwej", aname, avalue, &hFl,&hErr)){
		  if((hErr) || (hFl<-50.0) || (hFl>100.0)){
			  ERR("Nie wartosc temperatury %s=\"%s\"",aname,avalue);
			  return KO;
		  }
		  hFl*=10.0;
		  pp->tin=hFl;
          mask|=1;
	  }
	  if(czyt_Float("tempwyj", aname, avalue, &hFl,&hErr)){
		  if((hErr) || (hFl<-32767) || (hFl>32768)){
			  ERR("Nie wartosc temperatury %s=\"%s\"",aname,avalue);
			  return KO;
		  }
		  hFl*=10.0;
		  pp->tout=hFl;
          mask|=2;
	  }
   }

   if(mask != 3){
       ERR("Brak wszystkich wymaganych atrybutow");
	   return KO;
   }

   return OK;
}




int czytkrzy_CzytajTempWej(xmlTextReaderPtr reader, TKrzTemp* ptin)
{
   char* aname;
   char* avalue;
   int ret=OK;
   int tempIdx;
   int mask=0;

   while(xmlTextReaderMoveToNextAttribute(reader))
   {
      aname  = (char*)xmlTextReaderName(reader);
      avalue = (char*)xmlTextReaderValue(reader);

      if(czytkrzy_CzytajWsp(aname, avalue, &ptin->wsp, &mask)!=OK){
    	  return KO;
      }

      if(strcmp(aname,"tempid")==0){
         tempIdx=TEMP_tempIndex(avalue);
         if(tempIdx<0){
             ERR("Nieprawidlowy index temperatury: %s=\"%s\"",aname,avalue);
        	 return KO;
         }
         ptin->tIdx=tempIdx;
         mask|=1;
      }
   }

   if(mask != 7){
       ERR("Brak wszystkich wymaganych atrybutow");
	   return KO;
   }

   return OK;
}


int czytkrzy_CzytajWsp(char* pName, char* pValue, TKrzWsp* pwsp, int* mask){
    int    ret=OK;
	int    itmp;
    if(strcmp(pName,"mnoznik")==0){
	   float  fl;
	   itmp=sscanf(pValue,"%f",&fl);
	   if(itmp!=1){
          ERR("Nieprawidlowy mnoznik: %s=\"%s\"",pName,pValue);
   	      return KO;
	   }else{
		  pwsp->A=fl*KRZY_MNOZNIKWSP;
		  *mask|=2;
	   }
    }

    if(strcmp(pName,"offset")==0){
	   float  fl;
	   itmp=sscanf(pValue,"%f",&fl);
	   if(itmp!=1){
          ERR("Nieprawidlowy offset: %s=\"%s\"",pName,pValue);
   	      return KO;
	   }else{
		  pwsp->B=fl*KRZY_OFFSETWSP;
		  *mask|=4;
	   }
    }

	return ret;
}




//---------------------------------------------------------------
// some helpers
static U8 czyt_Float(char* pAtrName, char* pName, char* pValue, float* pVal, U8* pErr){
	int itmp;
	*pErr=0;
    if(strcmp(pName,pAtrName)==0){
	   float  fl;
	   itmp=sscanf(pValue,"%f",&fl);
	   if(itmp!=1) *pErr=1;
	   else        *pVal=fl;
       return 1;
    }
	return 0;
}

//---------------------------------------------------------------
// some helpers
static U8 czyt_Int(char* pAtrName, char* pName, char* pValue, int* pVal, U8* pErr){
	int itmp;
	*pErr=0;
    if(strcmp(pName,pAtrName)==0){
	   int  in;
	   itmp=sscanf(pValue,"%d",&in);
	   if(itmp!=1) *pErr=1;
	   else        *pVal=in;
	   return 1;
    }
	return 0;
}



int CZYT_ProcessKrzywa(xmlTextReaderPtr reader,const xmlChar *name, TKrzKrzywa**  ppkrzy,
		               int*   linKrzywa, int  depth, int  nodetype, int* idx)
{
    char*        aname;
    char*        avalue;
    int          i;
    U8           hErr;
    static int   nr;
    static int   linTempwej;
    static int   linFunkcja;
    static int   linKorekcja;
    static int   punktycnt;
    static int   ileKrzywych = KRZY_MAXDEFINICJIKRZYWYCH + 1;
    TKrzKrzywa*  pkrza;
    static S16   lastTemp;

    pkrza = *ppkrzy;

    if(ileKrzywych == 0){
        ERR("Za durzo krzywych (max=%d) !!!",KRZY_MAXDEFINICJIKRZYWYCH);
        return 1;
    }

	//----------------------------------------------
    // krzywe
    //----------------------------------------------
    //IF(poczatek definicji krzywej)
    if((depth==1)&&(nodetype==1)&&(strcmp(name,"krzywa")==0)&&(!*linKrzywa)){
       *linKrzywa=1;
       linTempwej=0;
       linFunkcja=0;
       linKorekcja=0;
       *idx=-1;

       //-------------------------------------
       // wstepne zerowanie danych
       for(i=0;i<KRZY_MAXTIN;i++) pkrza->tin[i].tIdx=TEMPDEF_INVALID_IDX;
	   for(i=0;i<KRZY_MAXODCINKOW;i++){
			pkrza->punkty[i].tin=-999;
			pkrza->punkty[i].tout=0;
	   }

       while(xmlTextReaderMoveToNextAttribute(reader))
       {
           aname  = (char*)xmlTextReaderName(reader);
           avalue = (char*)xmlTextReaderValue(reader);

           //1. numer
           if(czyt_Int("numer", aname, avalue, &i,&hErr)){
               if((hErr) || (i<1) || (i>KRZY_MAXKRZYWYCH)){
                   ERR("Nie poprawna wartosc %s=\"%s\" (1..%d)",aname,avalue,KRZY_MAXKRZYWYCH);
                   return 1;
               }
               pkrza->numer = i;
    		   for(i=0;i<KRZY_MAXTKOR;i++) pkrza->tkor[i].tIdx=TEMPDEF_INVALID_IDX;
               *idx=i;
           }

           //2. opis
           if(strcmp(aname,"opis")==0){
              if(*idx<0){
                  ERR("Brak numeru krzywej, pole \"numer\" musi byc pierwsze");
                  *linKrzywa=0;
                  return 1;
              }else{
                  snprintf(pkrza->opis,KRZY_MAXOPIS,"%s",avalue);
              }
           }

           //3. program
           if(czyt_Int("program", aname, avalue, &i,&hErr)){
               if((hErr) || (i<1) || (i>PROG_MAXPROGRAMOW)){
                   ERR("Nie poprawna wartosc %s=\"%s\" (1..%d)",aname,avalue,PROG_MAXPROGRAMOW);
                   return 1;
               }
               pkrza->program = i;
           }

       }

       if(*idx<0){
           ERR("Brak numeru programu, pole \"numer\"");
           *linKrzywa=0;
           return 1;
       }
       if(M_verbose){
           printf("------------------------------------------------\n");
           printf("Krzywa nr:%d  \"%s\"\n",pkrza->numer,pkrza->opis);
       }
    }
    //ENDIF(poczatek definicji krzywej)


    //IF(in krzywa)
    if(*linKrzywa){
       if((nodetype==15)&&(strcmp(name,"krzywa")==0)){
           *linKrzywa=0;
           *ppkrzy = (pkrza + 1);
           if(M_verbose) printf("\n");
           if(ileKrzywych) ileKrzywych--;
       }else{

    	   if((depth==2)&&(nodetype==1)&&
    	      (strcmp(name,"temperaturywej")==0)&&(!linTempwej))  {
    		   linTempwej=1;
    		   nr=0;
			   if(M_verbose)  printf("\n  Temperatury wejsciowe: \n");
			   pkrza->offsettin=0;
			   //Offset
			   while(xmlTextReaderMoveToNextAttribute(reader))
               {
                   float fl;
				   aname  = (char*)xmlTextReaderName(reader);

                   avalue = (char*)xmlTextReaderValue(reader);
				   if(czyt_Float("offset", aname, avalue, &fl,&hErr)){
					   if(hErr){
						   ERR("Nie poprawna wartosc %s=\"%s\"",aname,avalue);
						   return 1;
					   }
  				       pkrza->offsettin=fl*KRZY_OFFSETWSP;
				   }
              }
 		      if(M_verbose)  printf("    Offset glowny=%0.1f \n",(float)pkrza->offsettin/KRZY_OFFSETWSP);
    	   }
    	   if((nodetype==15)&&(strcmp(name,"temperaturywej")==0)) {linTempwej=0;}




    	   if((depth==2)&&(nodetype==1)&&
    	      (strcmp(name,"funkcja")==0)&&(!linFunkcja))  {
    		   linFunkcja=1;
    		   nr=0;
    		   lastTemp=-1000;
    		   punktycnt=0;
			   if(M_verbose)  printf("\n  Funkcja przejscia (punkty krzywej): \n");
    	   }
    	   if((nodetype==15)&&(strcmp(name,"funkcja")==0)) {
    		   linFunkcja=0;
    		   if(punktycnt<2){
				   ERR("Krzywa musi miec przynajmniej 2 punkty!");
				   return 1;
    		   }
    	   }



    	   if((depth==2)&&(nodetype==1)&&
    	      (strcmp(name,"korekcjawyjscia")==0)&&(!linKorekcja))  {
    		   linKorekcja=1;
    		   nr=0;
    		   lastTemp=-1000;
			   if(M_verbose)  printf("\n  Korekcja offsetu(odcinki krzywej): \n");
			   //Offset
			   while(xmlTextReaderMoveToNextAttribute(reader))
               {
                   float fl;
				   aname  = (char*)xmlTextReaderName(reader);
                   avalue = (char*)xmlTextReaderValue(reader);
				   if(czyt_Float("offset", aname, avalue, &fl,&hErr)){
					   if(hErr){
						   ERR("Nie poprawna wartosc %s=\"%s\"",aname,avalue);
						   return 1;
					   }
  				       pkrza->offsetkorekcja=fl*KRZY_OFFSETWSP;
				   }
              }
 		      if(M_verbose)  printf("    Offset glowny=%0.1f \n",(float)pkrza->offsetkorekcja/KRZY_OFFSETWSP);
    	   }
    	   if((nodetype==15)&&(strcmp(name,"korekcjawyjscia")==0)) {linKorekcja=0;}

    	   //IF(temp wejsciowa)
    	   if((linTempwej) && (nodetype==1) && (strcmp(name,"temperaturawej")==0)){
               if(nr>=KRZY_MAXTIN){
                   ERR("Zbyt wiele tempertur wejsciowych, maks. %d", KRZY_MAXTIN);
            	   return 1;
               }
               if(czytkrzy_CzytajTempWej(reader, &pkrza->tin[nr])!=OK){
            	   return 1;
               }
			   if(M_verbose){
				  printf("    Temp.Wej  temp.idx=\"%s\"  A=%5.3f, B=%5.1f \n",
						      TEMP_getTempIdxStr(pkrza->tin[nr].tIdx),
							  (float)(pkrza->tin[nr].wsp.A)/KRZY_MNOZNIKWSP,
							  (float)(pkrza->tin[nr].wsp.B)/KRZY_OFFSETWSP);
			   }
               nr++;
    	   }
    	   //ENDIF(temp wejsciowa)


    	   //IF(odcinek krzywej)
    	   if((linFunkcja) && (nodetype==1) && (strcmp(name,"punkt")==0)){
               if(punktycnt>=KRZY_MAXODCINKOW){
                   ERR("Zbyt wiele punktow, maks. %d",KRZY_MAXODCINKOW);
            	   return 1;
               }
    		   if(czytkrzy_CzytajPunkt(reader, &pkrza->punkty[nr])!=OK){
            	   return 1;
               }
               if((lastTemp>pkrza->punkty[nr].tin) && (nr > 0)){
                   ERR("Temperatura graniczna odcinka musi rosnac o 1st min.");
            	   return 1;
               }
               lastTemp=pkrza->punkty[nr].tin+1;
               if(nr>0){
            	   pkrza->wsp[nr-1]=KRZY_MNOZNIKWSP*
            			  (((float)pkrza->punkty[nr].tout - (float)pkrza->punkty[nr-1].tout)/
            			   ((float)pkrza->punkty[nr].tin - (float)pkrza->punkty[nr-1].tin));
               }
			   if(M_verbose){
				  printf("    Punkt temp.wej=%5.1f temp.wyj=%5.1f",
                              (float)(pkrza->punkty[nr].tin)*0.1,
                              (float)(pkrza->punkty[nr].tout)*0.1);
                  if(nr>0) printf("   wsp.odcinka=%0.3f",(float)pkrza->wsp[nr-1]/KRZY_MNOZNIKWSP);
				  printf("\n");
			   }
               nr++;
               punktycnt++;
    	   }
    	   //ENDIF(odcinek krzywej)


    	   //IF(temp korekcjawyjscia)
    	   if((linKorekcja) && (nodetype==1) && (strcmp(name,"korekcja")==0)){
               if(nr>=KRZY_MAXTKOR){
                   ERR("Zbyt wiele tempertur korekcyjnych, maks. %d",KRZY_MAXTKOR);
            	   return 1;
               }
               if(czytkrzy_CzytajTempWej(reader, &pkrza->tkor[nr])!=OK){
            	   return 1;
               }
			   if(M_verbose){
				  printf("    korekcja  temp.idx=\"%s\" A=%5.3f, B=%5.1f \n",
						      pkrza->tkor[nr].tIdx!=TEMPDEF_INVALID_IDX ?
						    	  TEMP_getTempIdxStr(pkrza->tkor[nr].tIdx):"  -  ",
							  (float)(pkrza->tkor[nr].wsp.A)/KRZY_MNOZNIKWSP,
							  (float)(pkrza->tkor[nr].wsp.B)/KRZY_OFFSETWSP);
			   }
               nr++;
    	   }
    	   //ENDIF(temp korekcjawyjscia)


       }
    }
    //ENDIF(in krzywa)

    return 0;
}
